/*
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *    2010 year
 *    Author: Michael Khromov aka MikeDM (Mike D'Morto)
 *    E-Mail: mikedmorto@gmail.com
 */
#include "dbthread.h"
#include <QApplication>

DbThread::DbThread(QString server, QString user, QString pass, QString dbname, QObject *parent) :
    QThread(parent)
{
    // сохраняем настройки
    this->dserver=server;
    this->duser=user;
    this->dpass=pass;
    this->dname=dbname;
    this->execq=false;
    this->isFun=false;
    this->stop=false;
    this->start();
}

void DbThread::stopThread(){
    this->stop=true;
}

void DbThread::putQuery(const QString &data){
    this->quer=data;
    this->execq=true;
}

void DbThread::putFun(const QString pl_name, const QString &fname, const QList<QVariant> &parameters){
    emit this->sigThPutLog(TH,3,"Debug: have function");
    // принимаем запрос и складываем его в очередь, которую в свою очередь обработает отдельный поток.
    FList list;
    list.fname=fname;
    list.parameters=parameters;
    list.pl_name=pl_name;
    this->flist.append(list);
    this->isFun=true;
}

void DbThread::putFunS(const QString pl_name, const QString &fname, const QList<QVariant> &parameters){
    // ложим запрос от плагина. синхронный режим
    emit this->sigThPutLog(TH,3,"Debug: Sync have function");
    this->flistS.pl_name=pl_name;
    this->flistS.fname=fname;
    this->flistS.parameters=parameters;
    this->isFunS=true;

}
// синхронный режим
void DbThread::putSinq(const QString &pl_name){
    this->sinqS.append(pl_name);
}

void DbThread::stopSinq(const QString &pl_name){
    // удаляем из списка плагинов запрашивающих работу в синхронном режиме.
    this->sinqS.removeFirst();

}

// переопределяем метод рун. это и есть отдельный поток.

void DbThread::run(){
    // значится тута старт. соеденяемся с базой и при положительном результате впадаем в бесконечный цикл ожидания
    // разрешения на запуск запроса. соединение с бд живет тут.
    emit this->sigThPutLog(TH,3,"Debug: create database connection");
    QSqlDatabase tdb = QSqlDatabase::addDatabase("QMYSQL");

    tdb.setHostName(this->dserver);
    tdb.setDatabaseName(this->dname);
    tdb.setUserName(this->duser);
    tdb.setPassword(this->dpass);

    if(!tdb.open()){
        // база данных не открылась. беда беда. возвращяем неправду.

        emit this->sigError("INIT",tdb.lastError().text());
        emit this->sigThPutLog(TH,3,"Debug: failure database connect");
        QApplication::processEvents();
    }else{
        emit sigFinish(NULL,NULL);
        QApplication::processEvents();
        // бесконечный цикл
        emit this->sigThPutLog(TH,3,"Debug: success database connect");
    }
    // маркер идентификатор
    int curid=0;
    for(;;){
        // ложимся поспать на 100 милисекунд. (1000 мс в с)
        this->msleep(1);
        QApplication::processEvents();
        // проверяем флаг остановки
        if(this->stop){
            qDebug()<<"close db connection";
            tdb.close();
            this->terminate();
        }

        // синхронный режим имеет приоритет выше.
        // пока существует очередь запросов на синхронный режим асинхронному ловить нечего
        // конечно очередь запростов в асинхронном режиме будет расти, но будет обработана только когда
        // освободиться очередь синхронных запросов.
            if(this->sinqS.size()>0){
                // в очереди кто то есть =) смотрим есть ли функция
                if(this->isFunS){
                    // функция есть, понеслась делать запрос
                    // исполняем функцию
                    // сначала проверяем есть ли такая функция для такого плагина.
                    // вынимаем тело функции
                    this->isFunS=false;
                    emit this->sigThPutLog(TH,3,"Debug: Sync first query, get functions of plugins list");
                    // вынимаем первую из списка функций. и работаем с этими данными
                    FList list=this->flistS;
                    QSqlQuery queryF("SELECT count(*) FROM `core_plugins`,`db_aq`"
                                    "where `core_plugins`.`id`=`db_aq`.`id_plugin`"
                                    "and `core_plugins`.`name`='"+
                                    list.pl_name+"' and `db_aq`.`qname`='"+
                                    list.fname+"'");

                    if(!queryF.exec()){
                        // запрос не прошел, беда йопт. сигнализируем ошибку.

                        emit this->sigErrorS(list.pl_name,queryF.lastError().text());
                    }else{
                    // забираем результат и проверяем

                    queryF.next();
                    if(!queryF.value(0).toBool()){
                        // функции такой для такого плагина нет. иннах отседа =)
                        emit this->sigErrorS(list.pl_name,"Not avarable Function <"+list.fname
                                            +"> to plugin <"+list.pl_name+">");
                    }else{

                        emit this->sigThPutLog(TH,3,"Debug: second query, get body ");
                       QSqlQuery query("SELECT `db_aq`.`body` FROM `core_plugins`,`db_aq`"
                                       "where `core_plugins`.`id`=`db_aq`.`id_plugin`"
                                       "and `core_plugins`.`name`='"+list.pl_name
                                       +"' and `db_aq`.`qname`='"+list.fname+"'");
                       if(!query.exec()){
                           // запрос не прошел, беда йопт. сигнализируем ошибку.
                           emit this->sigErrorS(list.pl_name,query.lastError().text());
                       }else{
                       // забираем тело.
                       query.next();
                       QString body=query.value(0).toString();
                       emit this->sigThPutLog(TH,3,"Debug: Sync body <"+body+">");
                       //qDebug()<<"BODY="<<body;
                       // теперь надо подставить в тело наши параметры.
                       // 1 символ в теле, это символ подстановки значений. читаем этот символ.
                       QString znak=body.mid(0,1);
                       //qDebug()<<znak;
                       // показываем запрос с выплненной подстановкой.
                       QString telo=body.mid(1,body.size()-1);
                       //qDebug()<<telo;
                       int znakcount=telo.count(znak);
                       //qDebug()<<"count znaks="<<znakcount;
                       // делаем проверку соответствия количество вхождений знака в тело и переданных параметров
                       if(znakcount != list.parameters.count()){
                           // количество параметров не совпадает, обшибко
                           emit this->sigErrorS(list.pl_name,"failure parameters");
                       }else{
                           // все гуд подставляем.
                           int j = 0;
                           int i = 0;
                           while ((j = telo.indexOf(znak, j)) != -1) {
                               //qDebug() << "ZNAK " << j;
                               telo.replace(j,1,this->sqlsafe(list.parameters.at(i).toString()));
                               ++j;
                               i++;
                           }

                           emit this->sigThPutLog(TH,3,"Debug: Sync query <"+telo+">");

                           // получили тело запроса, отправляемс
                           QSqlQuery query(telo);
                           if(!query.exec()){
                               // запрос не прошел, беда йопт. сигнализируем ошибку.

                               emit this->sigErrorS(list.pl_name,tdb.lastError().text());

                           }else{
                           // вынимаем запрос и отправляем по назначению
                           this->exportQuery=query;
                           emit this->sigFinishS(list.pl_name,&this->exportQuery);
                           }
                       }
                       }
                }
                }

                }
            }else{


        // ****************************************

        // а это уже асинхронный режим.
        if(this->isFun){
            // исполняем функцию
            // сначала проверяем есть ли такаяфункция для такого плагина.
            // вынимаем тело функции
            this->isFun=false;
            emit this->sigThPutLog(TH,3,"Debug: first query, get functions of plugins list");
            // вынимаем первую из списка функций. и работаем с этими данными
            FList list=this->flist.takeFirst();
            QSqlQuery queryF("SELECT count(*) FROM `core_plugins`,`db_aq`"
                            "where `core_plugins`.`id`=`db_aq`.`id_plugin`"
                            "and `core_plugins`.`name`='"+
                            list.pl_name+"' and `db_aq`.`qname`='"+
                            list.fname+"'");

            if(!queryF.exec()){
                // запрос не прошел, беда йопт. сигнализируем ошибку.

                emit this->sigError(list.pl_name,queryF.lastError().text());
            }else{
            // забираем результат и проверяем

            queryF.next();
            if(!queryF.value(0).toBool()){
                // функции такой для такого плагина нет. иннах отседа =)
                emit this->sigError(list.pl_name,"Not avarable Function <"+list.fname
                                    +"> to plugin <"+list.pl_name+">");
            }else{

                emit this->sigThPutLog(TH,3,"Debug: second query, get body ");
               QSqlQuery query("SELECT `db_aq`.`body` FROM `core_plugins`,`db_aq`"
                               "where `core_plugins`.`id`=`db_aq`.`id_plugin`"
                               "and `core_plugins`.`name`='"+list.pl_name
                               +"' and `db_aq`.`qname`='"+list.fname+"'");
               if(!query.exec()){
                   // запрос не прошел, беда йопт. сигнализируем ошибку.
                   emit this->sigError(list.pl_name,query.lastError().text());
               }else{
               // забираем тело.
               query.next();
               QString body=query.value(0).toString();
               emit this->sigThPutLog(TH,3,"Debug: body <"+body+">");
               //qDebug()<<"BODY="<<body;
               // теперь надо подставить в тело наши параметры.
               // 1 символ в теле, это символ подстановки значений. читаем этот символ.
               QString znak=body.mid(0,1);
               //qDebug()<<znak;
               // показываем запрос с выплненной подстановкой.
               QString telo=body.mid(1,body.size()-1);
               //qDebug()<<telo;
               int znakcount=telo.count(znak);
               //qDebug()<<"count znaks="<<znakcount;
               // делаем проверку соответствия количество вхождений знака в тело и переданных параметров
               if(znakcount != list.parameters.count()){
                   // количество параметров не совпадает, обшибко
                   emit this->sigError(list.pl_name,"failure parameters");
               }else{
                   // все гуд подставляем.
                   int j = 0;
                   int i = 0;
                   while ((j = telo.indexOf(znak, j)) != -1) {
                       //qDebug() << "ZNAK " << j;
                       telo.replace(j,1,this->sqlsafe(list.parameters.at(i).toString()));
                       ++j;
                       i++;
                   }

                   emit this->sigThPutLog(TH,3,"Debug: query <"+telo+">");

                   // получили тело запроса, отправляемс
                   QSqlQuery query(telo);
                   if(!query.exec()){
                       // запрос не прошел, беда йопт. сигнализируем ошибку.

                       emit this->sigError(list.pl_name,tdb.lastError().text());

                   }else{
                   // вынимаем запрос и отправляем по назначению
                   this->exportQuery=query;
                   curid++;
                   this->qlist.insert(curid,exportQuery);
                   emit sigFinish(curid,&this->exportQuery);
                   }
               }
           }
        }
        }
        }

        }
    }
}

void DbThread::clearID(int id){
    // удаляем из хранилища ответов уже отработанный

    QSqlQuery q = this->qlist.take(id);
}

QString DbThread::sqlsafe(const QString &str){
        QString aa = str;
        aa.replace("\\","\\\\");
        aa.replace("\"","\\\"");
        aa.replace("'","\\'");
        return aa;
}


